#include <fstream>
#include <string>
#include <vector>
#include <iostream>
#include <sstream>
#include <stdio.h>
#define MAX 9999999.0

template <typename T>
int serial_read_GridSize(const std::string &filename, int &nx, int &ny, int &nz, \
T &gx, T &gy, T &gz, bool readRealSize = false)
{
    // Open the file specified by `filename` for reading.
    std::ifstream file(filename);
    if (!file.is_open())
    {
        // If the file cannot be opened, print an error message and return 1 to indicate failure.
        std::cerr << "Error: could not open file " << filename << std::endl;
        return 1;
    }

    // Declare a string to store lines read from the file.
    std::string line;
    char *str;
    bool found_specgrid = false, found_coord = false, found_zcorn = false;

    T gxmin = MAX, gxmax = -MAX, gymin = MAX, gymax = -MAX, gzmin = MAX, gzmax = -MAX;
    T x, y, z;

    // Search for the line containing the keyword "SPECGRID" to find grid dimensions.
    while (std::getline(file, line))
    {
        if (!found_specgrid && line.find("SPECGRID") != std::string::npos)
        {
            // Read the next line containing grid dimensions.
            std::getline(file, line);
            str = const_cast<char*>(line.c_str());
            // std::istringstream(line) >> nx >> ny >> nz;
            nx = std::strtol(str, &str, 10);
            ny = std::strtol(str, &str, 10);
            nz = std::strtol(str, &str, 10);
            found_specgrid = true;
        }

        if (!found_coord && line.find("COORD") != std::string::npos)
        {
            for (int i = 0; i < (nx + 1) * (ny + 1); ++i)
            {
                std::getline(file, line);
                // std::istringstream(line) >> x >> y;
                str = const_cast<char*>(line.c_str());
                x = std::strtod(str, &str);
                y = std::strtod(str, &str);
                gxmin = std::min(gxmin, x);
                gxmax = std::max(gxmax, x);
                gymin = std::min(gymin, y);
                gymax = std::max(gymax, y);
            }
            found_coord = true;
        }

        if (!found_zcorn && line.find("ZCORN") != std::string::npos)
        {
            std::getline(file, line, '/');
            str = const_cast<char*>(line.c_str());
            for (int i = 0; i < nx * ny * nz * 8; ++i)
            {
                // iss >> z;
                z = std::strtod(str, &str);
                gzmin = std::min(gzmin, z);
                gzmax = std::max(gzmax, z);
            }
            found_zcorn = true;
        }

        if (found_specgrid && found_coord && found_zcorn)
            break;
    }

    // Close the file after reading all required data.
    file.close();

    // Calculate the real sizes of the grid in x, y, and z directions.
    gx = gxmax - gxmin;  // Range of x-coordinates.
    gy = gymax - gymin;  // Range of y-coordinates.
    gz = gzmax - gzmin;  // Range of z-coordinates.

    // Return 0 to indicate successful execution.
    return 0;
}

template <typename T>
int serial_read_Porps(const std::string& filename, std::vector<T>& youngmods, std::vector<T>& poissonrs, int nx, int ny, int nz)
{
    // Open the file specified by `filename` for reading.
    std::ifstream file(filename);
    if (!file.is_open())
    {
        // If the file cannot be opened, print an error message and return 1 to indicate failure.
        std::cerr << "Error: could not open file " << filename << std::endl;
        return 1;
    }

    // Declare a string to store lines read from the file.
    std::string line;

    // Search for the section containing Young's modulus ("YOUNGMOD").
    while (std::getline(file, line))
    {
        if (line.find("YOUNGMOD") != std::string::npos)
        {
            // Read the line of data after the "YOUNGMOD" keyword until the delimiter '/'.
            std::getline(file, line, '/');
            std::istringstream iss(line);  // Create a string stream for parsing the data.
            std::string tmp;              // Temporary string to hold each token.
            int cnt = 0;                  // Counter to track the position in the `youngmods` array.

            // Parse the data in the format `<num>*<value>` (e.g., `3*5.2`).
            while (iss >> tmp)
            {
                // Split the token into `<num>` and `<value>` parts.
                auto pos = tmp.find('*');           // Find the position of the '*' character.
                int num = std::stoi(tmp.substr(0, pos)); // Extract `<num>` (number of repetitions).
                T val = std::stof(tmp.substr(pos + 1));  // Extract `<value>` (actual data value).

                // Fill the `youngmods` array with `<value>` repeated `<num>` times.
                for (int i = 0; i < num; i++)
                {
                    youngmods[cnt++] = val;  // Assign the value to the array and increment the counter.
                }
            }
        }

        // Search for the section containing Poisson's ratio ("POISSONR").
        if (line.find("POISSONR") != std::string::npos)
        {
            // Read the line of data after the "POISSONR" keyword until the delimiter '/'.
            std::getline(file, line, '/');
            std::istringstream iss(line);  // Create a string stream for parsing the data.
            std::string tmp;              // Temporary string to hold each token.
            int cnt = 0;                  // Counter to track the position in the `poissonrs` array.

            // Parse the data in the format `<num>*<value>` (e.g., `3*0.25`).
            while (iss >> tmp)
            {
                // Split the token into `<num>` and `<value>` parts.
                auto pos = tmp.find('*');           // Find the position of the '*' character.
                int num = std::stoi(tmp.substr(0, pos)); // Extract `<num>` (number of repetitions).
                T val = std::stof(tmp.substr(pos + 1));  // Extract `<value>` (actual data value).

                // Fill the `poissonrs` array with `<value>` repeated `<num>` times.
                for (int i = 0; i < num; i++)
                {
                    poissonrs[cnt++] = val;  // Assign the value to the array and increment the counter.
                }
            }
            break;  // Exit the loop after processing "POISSONR".
        }
    }

    // Close the file after reading all required data.
    file.close();

    // Return 0 to indicate successful execution.
    return 0;
}


template <typename T>
int serial_write_Props(const std::string& filename, std::vector<T>& youngmods, std::vector<T>& poissonrs, int nx, int ny, int nz)
{
    // Open the file specified by `filename` for writing.
    FILE* file = fopen(filename.c_str(), "w");
    if (file == NULL)
    {
        // If the file cannot be opened, print an error message and return 1 to indicate failure.
        std::cerr << "Error: could not open file " << filename << std::endl;
        return 1;
    }

    // Write the "YOUNGMOD" section to the file.
    fprintf(file, "YOUNGMOD\n");
    int cnt = 0;  // Counter to iterate over the `youngmods` array.

    // Iterate through all elements in the `youngmods` array.
    while (cnt < nx * ny * nz)
    {
        T val = youngmods[cnt++];  // Retrieve the current Young's modulus value.
        int num = 1;               // Initialize the count of repeated values.

        // Count consecutive occurrences of the same value in the array.
        while (cnt < nx * ny * nz && val == youngmods[cnt])
        {
            num++;  // Increment the count for each match.
            cnt++;  // Move to the next element in the array.
        }

        // Write the repetition format `<num>*<value>` to the file.
        fprintf(file, "%d*%f ", num, val);
    }
    // End the "YOUNGMOD" section with a '/' character.
    fprintf(file, "/\n");

    // Write the "POISSONR" section to the file.
    fprintf(file, "POISSONR\n");
    cnt = 0;  // Reset the counter to iterate over the `poissonrs` array.

    // Iterate through all elements in the `poissonrs` array.
    while (cnt < nx * ny * nz)
    {
        T val = poissonrs[cnt++];  // Retrieve the current Poisson's ratio value.
        int num = 1;               // Initialize the count of repeated values.

        // Count consecutive occurrences of the same value in the array.
        while (cnt < nx * ny * nz && val == poissonrs[cnt])
        {
            num++;  // Increment the count for each match.
            cnt++;  // Move to the next element in the array.
        }

        // Write the repetition format `<num>*<value>` to the file.
        fprintf(file, "%d*%f ", num, val);
    }
    // End the "POISSONR" section with a '/' character.
    fprintf(file, "/\n");

    // Close the file after writing all data.
    fclose(file);

    // Return 0 to indicate successful execution.
    return 0;
}
